import findIndex from 'lodash/findIndex';
import last from 'lodash/last';
import setWith from 'lodash/setWith';
import find from 'lodash/find';
import result from 'lodash/result';
import omit from 'lodash/omit';
import reject from 'lodash/reject';
import hasIn from 'lodash/hasIn';
import isRegExp from 'lodash/isRegExp';
import each from 'lodash/each';
import update from 'immutability-helper';
import { getUniqueID } from '@/utils/utils';

// 下列所有方法若有用到tree,格式都如下:
// [
//   {
//     name: "0",
//     properties: [
//       {
//         name: "0-0",
//         rules: [
//           {
//             required: true,
//           },
//         ],
//       },
//       {
//         name: "0-1",
//         rules: [
//           {
//             required: true,
//           },
//         ],
//         properties: [
//           {
//             name: "0-1-0",
//             rules: [
//               {
//                 required: true,
//               },
//             ],
//           },
//           {
//             name: "0-1-1",
//             rules: [
//               {
//                 required: true,
//               },
//             ],
//           },
//         ],
//       },
//     ],
//   },
// ];

export function hasKey(obj, key) {
  return hasIn(obj, key);
}

/**
 *
 * @param {object[]} tree
 * @param {boolean} flag  true表示添加__id, false表示移除
 */
export function dealId(tree, flag = true) {
  if (!Array.isArray(tree)) {
    return [];
  }

  function walk(arr) {
    return arr.map(item => {
      if (flag) {
        if (!hasKey(item, 'properties')) {
          return { ...item, __id: getUniqueID('el') };
        }
        return { ...item, __id: getUniqueID('el'), properties: walk(item.properties) };
      }

      if (!hasKey(item, 'properties')) {
        return omit(item, ['__id']);
      }

      return { ...omit(item, ['__id']), properties: walk(item.properties) };
    });
  }

  return walk(tree);
}

/**
 *
 * @param {object[]} tree
 * @param {boolean} flag  true表示添加__id, false表示移除
 */
export function resetSpan(tree) {
  if (!Array.isArray(tree)) {
    return [];
  }

  function walk(arr) {
    return arr.map(item => {
      if (hasKey(item, 'properties')) {
        return { ...item, properties: walk(item.properties) };
      }

      const { megaProps = { span: 1 }, ...rest } = item;
      return { ...rest, megaProps: { ...megaProps, span: 1 } };
    });
  }

  return walk(tree);
}

/**
 *
 * @param {object[]} tree
 */
export function filterHideItem(tree) {
  if (!Array.isArray(tree)) {
    return [];
  }

  function walk(arr) {
    return arr
      .filter(item => item.display !== false)
      .map(item => {
        if (hasKey(item, 'properties')) {
          item.properties = walk(item.properties);
          return item;
        }
        return item;
      });
  }

  return walk(tree);
}

/**
 * 根据节点数据 获取它在树中的路径 如 '0-1-2'
 * @param {object[]} tree  树形结构的对象数组，后面所有的方法都是基于下列树结构的操作。
 * @param {*} node
 * @param {*} parentIndex
 * @return {string} 返回格式如： '0' 或 '0-1-1'的字符串。 没找到返回  '-1'
 */
export function getLevel(tree, node, parentIndex = '') {
  let result = -1;

  // 修复：findIndex()方法，第二个参数如果是空对象 会返回0
  if (Object.keys(node).length === 0) {
    return result;
  }

  const walk = (tree, node, parentIndex) => {
    if (Array.isArray(tree)) {
      const targetIndex = findIndex(tree, node);
      if (targetIndex !== -1) {
        result = parentIndex === '' ? `${targetIndex}` : `${parentIndex}-${targetIndex}`;
      }
      // eslint-disable-next-line consistent-return
      tree.forEach((item, index) => {
        if (hasKey(item, 'properties')) {
          return walk(
            item.properties,
            node,
            parentIndex === '' ? `${index}` : `${parentIndex}-${index}`
          );
        }
      });
      return result;
    }
    return result;
  };

  walk(tree, node, parentIndex);

  return result;
}

/**
 * 判断对象obj或者它的子节点的name 是否已经存在于tree中
 * @param {object[]} tree
 * @param {object} obj
 */
export function hasThisName(tree, obj) {
  function walk(tree, node) {
    if (node.name) {
      return getLevel(tree, { name: node.name }) !== -1;
    }

    if (hasKey(node, 'properties')) {
      return node.properties.some(item => {
        return walk(tree, item);
      });
    }

    return false;
  }

  return walk(tree, obj);
}

/**
 * 提取tree中name的值等于指定nameValue的节点
 * @param {*} tree
 * @param {string} nameValue
 * @return { object }
 */
export function getNodeByName(tree, nameValue) {
  let result = null;

  function walk(tree, key) {
    tree.forEach(item => {
      if (hasKey(item, 'name') && item.name === key && item.name) {
        result = item;
      }
      if (hasKey(item, 'properties')) {
        walk(item.properties, key);
      }
    });
  }
  walk(tree, nameValue);
  return result;
}

/**
 * 提取tree中name的值等于指定nameValue的节点
 * @param {*} tree
 * @param {string} nameValue
 * @return { object }
 */
export function getNodeById(tree, id) {
  let result = null;

  function walk(tree, id) {
    // eslint-disable-next-line consistent-return
    each(tree, item => {
      if (hasKey(item, 'id') && item.id === id && item.id !== '') {
        result = item;
        return false;
      }
      if (hasKey(item, 'properties')) {
        walk(item.properties, id);
      }
    });
  }

  walk(tree, id);

  return result;
}

/**
 * 提取取'0-1-2'中的最后一个数组
 *
 * @param {*} level
 */
export function pickLast(level) {
  // level格式必须是 '0' 或 '0-1-0'
  if (!/^\d(-\d+)*$/.test(level)) {
    return -1;
  }

  const arr = `${level}`.split('-');
  return parseInt(last(arr), 10);
}

/**
 *
 * @param {string} level 从 '0-1-2' 转成  'formSchema[0].properties[1].properties[2]' _.set和_.result可以使用的Path
 */
export function parseLevel(level) {
  const arr = `${level}`.split('-');

  const result = [];
  const tmp = 'properties[0]';

  if (arr.length === 0) {
    return 'formSchema';
  }
  if (arr.length === 1) {
    return 'formSchema[0]'.replace(/\d/, arr[0]);
  }
  if (arr.length > 1) {
    result.push('formSchema[0]'.replace(/\d/, arr[0]));
    arr.shift();
    arr.forEach(value => result.push(tmp.replace(/\d/, value)));
    return result.join('.');
  }
  return null;
}

/**
 * 根据level 获取节点数据
 * @param {object[]} tree
 * @param {string} level
 * @return {object} 返回指定路径的节点数据，没找到 返回undefined
 */
export function getNode(tree, level) {
  return result({ formSchema: tree }, parseLevel(level));
}
/**
 *
 * @param {*} level
 * @param {*} action  immutability-helper的所有办法，如：{ $splice: [ [0, 1], [1, 0,'a']] }
 */
export function getAction(level, action) {
  let obj = {};

  // level格式必须是 '0' 或 '0-1-0'
  if (!/^\d(-\d+)*$/.test(level)) {
    return obj;
  }

  const arr = `${level}`.split('-');
  // $splice 数组套数组，start,end, 插入的数据
  if (arr.length === 1) {
    obj = action;
  } else {
    // 删除最后一位
    let str = '';
    // [1,2,0] 移除最后一位 然后拼出 [1][properties][2][properties]
    update(arr, { $splice: [[arr.length - 1, 1]] }).forEach(function(value) {
      str += `[${value}][properties]`;
    });
    setWith(obj, str, action);
  }
  return obj;
}

/**
 * 判断对象数组arr中是否已存在包含指定key的对象，
 * 如果存在，移除旧对象，添加新对象
 * 不存在，添加新对象
 *
 * 返回新的对象数组
 * @param {object[]} arr
 * @param {string} key
 * @param {object} newItem
 */
export function addRule(arr, key, newItem) {
  const targetIndex = findIndex(arr, item => item[key] !== undefined);
  let newArr = [];

  if (targetIndex !== -1) {
    newArr = update(arr, {
      $splice: [[targetIndex, 1, newItem]],
    });
  } else {
    newArr = [...arr, newItem];
  }
  return newArr;
}
/**
 * 清除tree中 无效的节点
 *
 * 比如使用unset方法删除后，会在数组中留下占位（即长度不会变）
 *
 * update(['a', 'b', 'c', 'd', 'e'], { $unset: [1, 2] })  =>  [ 'a', <2 empty items>, 'd', 'e' ]
 *
 * cleanTree() 之后 变成  [ 'a', 'd', 'e' ]  lenght =3
 * @param {*} tree
 */
export function cleanTree(tree) {
  return tree
    .filter(item => item !== undefined)
    .map(item => {
      if (hasKey(item, 'properties')) {
        return { ...item, properties: cleanTree(item.properties) };
      }
      return item;
    });
}

/**
 *
 * @param {object[]} tree  树形结构的对象数组
 * @param {string} level '0-1-0' 表示节点在树中的层级，每个数字表示下标
 * @param {object} newItem 新的节点
 *
 * level = '0'
 * update(tree,  { $splice: [0, 1] } )
 *
 * level = '1-2-0'
 * update(arr,  { 1: { properties: { 2: { properties: { $splice: [[0, 1]] } } } } })
 *
 */
export function $$splice(tree, level, newItem) {
  // level格式必须是 '0' 或 '0-1-0'
  if (!Array.isArray(tree) || !/^\d(-\d+)*$/.test(level)) {
    return;
  }

  const arr = `${level}`.split('-');

  // $splice 数组套数组，start,end, 插入的数据
  if (arr.length === 1) {
    let actionA = [parseInt(arr[0], 10), 1];
    if (newItem) {
      actionA = [...actionA, newItem];
    }
    // eslint-disable-next-line consistent-return
    return update(tree, { $splice: [actionA] });
  }

  // 删除最后一位
  let str = '';

  // [1,2,0] 移除最后一位 然后拼出 [1][properties][2][properties]
  update(arr, { $splice: [[arr.length - 1, 1]] }).forEach(function(value) {
    str += `[${value}][properties]`;
  });

  // setWith会改变原对象
  const obj = {};
  let actionB = [parseInt(arr[arr.length - 1], 10), 1];

  if (newItem) {
    actionB = [...actionB, newItem];
  }

  setWith(obj, str, { $splice: [actionB] });

  // eslint-disable-next-line consistent-return
  return update(tree, obj);
}

/**
 * 删除数组中下标为 1 和 2 的两个元素，但是保留**占位**
 * update([1,2,3,4], { $unset: [1, 2] }) => [1, <2 empty items>, 4]
 *
 * @param {object[]} tree  树形结构的对象数组
 * @param {string} level '0-1-0' 表示节点在树中的层级，每个数字表示下标
 *
 *
 * level = '0'
 * update(tree,  { $unset: [0] } )
 *
 * level = '1-2-0'
 * update(arr,  { 1: { properties: { 2: { properties: { $unset: [0] } } } } })
 *
 */
export function $$unset(tree, level) {
  // level格式必须是 '0' 或 '0-1-0'
  if (!Array.isArray(tree) || !/^\d(-\d+)*$/.test(level)) {
    return;
  }

  const arr = `${level}`.split('-');

  if (arr.length === 1) {
    // eslint-disable-next-line consistent-return
    return update(tree, { $unset: [parseInt(arr[0], 10)] });
  }

  // 删除最后一位
  let str = '';

  // [1,2,0] 移除最后一位 然后拼出 [1][properties][2][properties]
  update(arr, { $splice: [[arr.length - 1, 1]] }).forEach(function(value) {
    str += `[${value}][properties]`;
  });

  // setWith会改变原对象
  const obj = {};
  setWith(obj, str, { $unset: [parseInt(last(arr), 10)] });

  // eslint-disable-next-line consistent-return
  return update(tree, obj);
}

/**
 * 往对应下标 添加节点，如果下标处无数据 则插入，若已有数据会被替换
 * @param {object[]} tree  树形结构的对象数组
 * @param {string} level '0-1-0' 表示节点在树中的层级，每个数字表示下标
 * @param {object} item  新增的节点
 *
 * level = '1'
 * update(tree, { 1: { $set: item } })
 *
 * level = '0-4'
 * update(arr,{0: { properties: { 4: { $set:{'componentType':'Input',id:'sdfd',label:'sdfd',name:'sdfd'} }}} })
 *
 */
export function $$set(tree, level, item) {
  // level格式必须是 '0' 或 '0-1-0'
  if (!Array.isArray(tree) || !/^\d(-\d+)*$/.test(level)) {
    return [];
  }

  const arr = `${level}`.split('-');

  // { 1 : { $set: item } } 往数组第2位 插入item
  if (arr.length === 1) {
    // eslint-disable-next-line consistent-return
    return update(tree, { [arr[0]]: { $set: item } });
  }

  // [1,2,1] 拼出 [1][properties][2][properties]
  const str = arr.join('[properties]');

  // setWith会改变原对象
  const obj = {};

  setWith(obj, str, { $set: item });

  // eslint-disable-next-line consistent-return
  return update(tree, obj);
}

export function isStrRegExp(str) {
  let result = false;

  try {
    result =
      str.length > 2 &&
      /^\//.test(str) &&
      /\/$/.test(str) &&
      isRegExp(new RegExp(str.replace(/\//g, '')));
    // eslint-disable-next-line no-empty
  } catch (e) {}

  return result;
}

/**
 * 提取当前tree中的所有字段 *
 * 返回数组 [{componetType,label,value,dataSource?}]
 */
export function getAllFieldName(tree) {
  const result = [];

  if (!Array.isArray(tree)) {
    return [];
  }

  function walk(arr) {
    arr.forEach(node => {
      if (hasKey(node, 'properties')) {
        walk(node.properties);
      }

      if (
        !find(result, { value: node.name }) &&
        `${node.componentType}`.toLocaleLowerCase() !== 'card'
      ) {
        let raw = { componentType: node.componentType, label: node.label, value: node.name };

        if (Array.isArray(node.dataSource)) {
          raw = {
            ...raw,
            dataSource: node.dataSource.map(item => ({ label: item.label, value: item.value })),
          };
        }
        result.push(raw);
      }
    });
  }

  walk(reject(tree, item => item.properties === undefined || item.properties.length === 0));
  return result;
}

export function getStepMarks(columnNum) {
  const mapping = {
    1: { 1: '1' },
    2: { 1: '1/2', 2: '1' },
    3: { 1: '1/3', 2: '2/3', 3: '1' },
    4: { 1: '1/4', 2: '2/4', 3: '3/4', 4: '1' },
  };

  return mapping[columnNum];
}
